/*
 *@# ParserCallback Created on 2004-8-9
 *
 *
 * Copyright: Bluejay Networks Inc. & Inquiry Management Systems Ltd.  2004
 *
 * This software is the proprietary information of Bluejay Networks Inc. & 
 * Inquiry Management Systems Ltd.  
 * Use is subject to license terms.
 */
package com.ims.iaa.html;

import com.ims.iaa.IaaDebug; 
import com.ims.iaa.IaaUtil; 
import com.ims.iaa.URLUtil;
import com.ims.iaa.AD.*; 

import javax.swing.text.html.*;
import javax.swing.text.*;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Enumeration;
import java.util.LinkedList;
import java.util.List;

/**
 *
 * This class handle HTML connect,
 * now it only handle script tag to capture pop-up advertisement.
 * pop-up advertisement usually through use javascript to show
 * a pop-up window, it must a function "window.open"
 * 
 * @see PageAnalyzer
 * 
 * @author ShengMeng Bai
 * 
 * @version 1.0
 * 
 */
public class ParserCallback
	extends HTMLEditorKit.ParserCallback {
    /*In windows, Internet Explorer show flash through find OBJECT tag,
    * if the CLASSID attribute of OBJECT tag is FLASH_CLASSID, this OBJECT
    * is flash object.
    */
    private final static String FLASH_CLASSID = "clsid:d27cdb6e-ae6d-11cf-96b8-444553540000";
    //The OBJECT tag has a PARAM name movie, this PARAM contain the URL of the flash.
    private final static String FLASH_MOVIE = "movie";
    
    //some flash show through javascript, so the parsed result may not contain object tag,
    //use embed tag to look for the falsh.
    private final static HTML.UnknownTag EMBED = new HTML.UnknownTag("embed");
	
	/*
	 * distingush TAG
	 */
	private final static int TAG_A = 1;
	private final static int TAG_OBJECT = 2;
	private final static int TAG_APPLET = 3;
	private final static int TAG_OTHER = -1;
	private int m_iCurTag = TAG_OTHER;

	private SimpleAttributeSet m_objAttributeSet = null;
	//current analyser page URL
	private URL m_pageURL;
	//because we get the sub page form frame and iframe tag.
	//so we must set the publishserURL. the publisherURL
	private URL m_publisherURL;
	
	private String m_sUserAgent;
	
	//when look up popup, use it to seek SCRIPT tag. Or for Applet Tag
	private int m_iTagStart = 0;
	
	//the count of image, flash and popup
	private int m_iImgLinkCount = 0;
	private int m_iFlashLinkCount = 0;
	private int m_iPopupCount = 0;
	private int m_iAppletLinkCount = 0;
	private int m_iHrefLinkCount = 0;//++by changsheng gong 
	
	private final ImpAnalyzer m_objImpAnalyzer;

	//the html content
	private final StringBuffer m_sbHtml;
	/** 
	 * if current web page are popup window, 
	 * m_sParentPage is the parent page of this popup window,
	 * and you should call #SetParentPageURLStr() to set m_sParentPage.
	 */ 
	private String m_sParentPage = null;
	/**
	 * it be setted in #SetParentPageURLStr
	 */
	private boolean m_bIsPopupPage;
	//we need judge the Agency for HrefLink at first
	private List m_lstAgency;
	
	/*
	 * This list use to save the image, flash url string
	 */
	private LinkedList m_urlList = new LinkedList();
	
	public ParserCallback(ImpAnalyzer impa, URL publisherURL, URL pageURL, StringBuffer sbHtml){
		m_objImpAnalyzer = impa;
		m_publisherURL = publisherURL;
		m_pageURL = pageURL;
		m_sbHtml = sbHtml;
		m_sUserAgent = impa.getUserAgent();
		//add by ShengMengBai at 2005-04-15 for href
		m_lstAgency = impa.getSpiderDataStream().getAgencyData();
	}
	
	/**
	 * Usually, a popup AD show a image or flash
	 * when parse a popup html, should judge whether it is AD or not,
	 * if the html too long, it shouldn't be AD, or if the html file
	 * contain more than one IMG tag or FLASh tag, it shouldn't be AD
	 * 
	 * @return true, if this html isn't popup, or if it is AD popup html.
	 * 		   false, if if this html isn't AD popup html.
	 */
	public boolean isValidHtml(){
		if (m_bIsPopupPage){
			/*
			 * if the popup is Ad, the html file should be very short,
			 * if it is very long, we won't deal with it.
			 * if this html size more than 1K, don't deal with it.
			 */
			int iLen = m_sbHtml.length();
			if (iLen > 1024){
	            IaaUtil.log("this popup html is too long, it shouldn't be AD.");
	            return false;
			}
		}
		return true;
	}
    
    public void SetPopupParentPageURLStr(String sURL){
    	m_bIsPopupPage = true;
    	m_sParentPage = sURL;
    }

	/**
	 * Called by the parser when a HTML comment is located. 
	 * The text of the comment is contained in data. 
	 * The parameter pos gives the current position.
	 */
	public void handleComment(char[] data,int pos)
	{
	}

	/**
	 * Called by the parser when an ending HTML tag is located. 
	 * The tag is contained in the parameter t. 
	 * The parameter pos gives the current position.
	 */
	public void handleEndTag(HTML.Tag t,int pos)
	{
		switch (m_iCurTag)
		{
		case TAG_A:
			m_iCurTag = TAG_OTHER;
			//++changsheng gong 2005-3-22
			findHrefLink();
	        //--changsheng gong 2005-3-22
			m_objAttributeSet = null;
			break;
		case TAG_OBJECT:
			m_iCurTag = TAG_OTHER;
			findFlashLink(true);
			m_objAttributeSet = null;
			break;
		case TAG_APPLET:
			findAppletLink();
			m_iCurTag = TAG_OTHER;
			m_objAttributeSet = null;
			break;
		}
	}

	/**
	 * Called by the parser when an error occurs. 
	 * The error message text is contained in errorMsg. 
	 * The parameter pos gives the current position.
	 */
	public void handleError(String errorMsg,int pos)
	{
	}

	/**
	 * Called by the parser when a simple tag is located. 
	 * The tag is contained in t; the list of attributes is contained in a. 
	 * The parameter pos gives the current position.
	 * Simple tags are sometimes ending tags. 
	 * If the parser encounters an ending tag for a tag type that it considers either simple or unknown, 
	 * handleSimpleTag will be called for the ending tag. 
	 * The only way to detect whether this is an ending tag or a true simple tag is 
	 * to check for the occurrence of the HTML.Attribute.ENDTAG attribute. 
	 * Only ending tags contain this attribute.
	 */
	public void handleSimpleTag(HTML.Tag t,
	                            MutableAttributeSet a,int pos)
	{
		switch (m_iCurTag)
		{
		case TAG_A:
			SimpleAttributeSet sas = (SimpleAttributeSet)m_objAttributeSet.getAttribute(t);
			if(sas != null){
				sas.addAttributes(a);
			} else {
				m_objAttributeSet.addAttribute(t, new SimpleAttributeSet(a.copyAttributes()));
			}
			//++Add by ShengMeng Bai at 2004-11-21
			//	because some A tag contain more than one img tag.
			if(t.equals(HTML.Tag.IMG)){
				findImgLink();
			}
			//--Add by ShengMeng Bai at 2004-11-21
			break;
		case TAG_OBJECT:
			if (t.equals(HTML.Tag.PARAM)){
				SimpleAttributeSet as = (SimpleAttributeSet)m_objAttributeSet.getAttribute(t);
				if(as == null){
					as = new SimpleAttributeSet();
					m_objAttributeSet.addAttribute(t, as);
				}
				Object name = a.getAttribute(HTML.Attribute.NAME);
				Object value = a.getAttribute(HTML.Attribute.VALUE);
				if(name != null && value != null){
					as.addAttribute(a.getAttribute(HTML.Attribute.NAME), a.getAttribute(HTML.Attribute.VALUE));
				}
			}
			break;
		case TAG_APPLET:
			if (t.equals(HTML.Tag.PARAM)){
				SimpleAttributeSet as = (SimpleAttributeSet)m_objAttributeSet.getAttribute(t);
				if(as == null){
					as = new SimpleAttributeSet();
					m_objAttributeSet.addAttribute(t, as);
				}
				Object name = a.getAttribute(HTML.Attribute.NAME);
				Object value = a.getAttribute(HTML.Attribute.VALUE);
				if(name != null && value != null){
					as.addAttribute(a.getAttribute(HTML.Attribute.NAME), a.getAttribute(HTML.Attribute.VALUE));
				}
			}
			break;
		default:
			//find embed tag
			if(m_iCurTag == TAG_OTHER && t.equals(EMBED)){
				m_objAttributeSet = new SimpleAttributeSet();
				m_objAttributeSet.addAttribute(t, new SimpleAttributeSet(a.copyAttributes()));
				findFlashLink(false);
				m_objAttributeSet = null;
			}
			break;
		}
	}

	/**
	 * Called by the parser when a starting HTML tag is located. 
	 * The tag is contained in the parameter t. 
	 * The parameter pos gives the current position.
	 */
	public void handleStartTag(HTML.Tag t,
	                           MutableAttributeSet a,int pos)
	{
		switch (m_iCurTag)
		{
		case TAG_A:
		case TAG_OBJECT:
			SimpleAttributeSet as = (SimpleAttributeSet)m_objAttributeSet.getAttribute(t);
			if(as != null){
				as.addAttributes(a);
			} else {
				m_objAttributeSet.addAttribute(t, new SimpleAttributeSet(a.copyAttributes()));
			}
			break;
		case TAG_APPLET:
			//do nothing.
			break;
		default:
			if (t.equals(HTML.Tag.A)){//<a>
				m_iCurTag = TAG_A;
				m_objAttributeSet = new SimpleAttributeSet();
				m_objAttributeSet.addAttribute(t, new SimpleAttributeSet(a.copyAttributes()));
			} else if (t.equals(HTML.Tag.OBJECT)){//<object>
				m_iCurTag = TAG_OBJECT;
				m_objAttributeSet = new SimpleAttributeSet();
				m_objAttributeSet.addAttribute(t, new SimpleAttributeSet(a.copyAttributes()));
			} else if (t.equals(HTML.Tag.APPLET)){//<aplet>
				m_iCurTag = TAG_APPLET;
				m_iTagStart = pos;
				m_objAttributeSet = new SimpleAttributeSet();
				m_objAttributeSet.addAttribute(t, new SimpleAttributeSet(a.copyAttributes()));
			}
			break;
		}
	}
	/**
	 * Called by the parser when text is located in the HTML document. 
	 * The text is contained in the parameter data. 
	 * The parameter pos gives the current position.
	 */
	public void handleText(char[] data,int pos){
	}
	
	public int getImageLinkCount(){
		return m_iImgLinkCount;
	}
	public int getFlashLinkCount(){
		return m_iFlashLinkCount;
	}
	public int getAppletLinkCount(){
		return m_iAppletLinkCount;
	}
	public int getPopupCount(){
		return m_iPopupCount;
	}

    private void findImgLink() {
        SimpleAttributeSet asTagA = (SimpleAttributeSet)m_objAttributeSet.getAttribute(HTML.Tag.A);
        if(asTagA == null){
        	return;
        }
        
        SimpleAttributeSet asTagIMG = (SimpleAttributeSet)m_objAttributeSet.getAttribute(HTML.Tag.IMG);
        if(asTagIMG == null){
        	return;
        }
        
        Object objHREF = asTagA.getAttribute(HTML.Attribute.HREF);
        if(objHREF == null){
        	return;
        }
        
	    //if connected to this URL, don't reconnect it.
        String sURL = null;
        try {
        	sURL = (String)asTagIMG.getAttribute(HTML.Attribute.SRC);
        } catch (Exception e) {
            IaaUtil.printStackTrace(e);
            return;
        }
	    if(sURL == null || sURL.trim().equals("") || m_urlList.contains(sURL)){
	    	return;
	    }else{
	    	m_urlList.add(sURL);
	    }

	    //connect to the image.
        IaaDebug.begin(1, "find ImgLink");
        IaaUtil.log("image in parsercallback"+objHREF.toString());
        IaaUtil.log("image pageurl in parsercallback"+m_pageURL);
        ImgLink imgLink = null;
        try {
            URL url = URLUtil.getURL(m_pageURL, sURL);
            if(m_objImpAnalyzer.isDisallow(url)){
                IaaUtil.log("Obey robots.txt: " + url);
                return;
            }
            imgLink = new ImgLink(url, m_pageURL, objHREF.toString());
        } catch (MalformedURLException e) {
            // we cannot construct the srcUrl or targetUrl of the image.
            // it may because it is written in javascript,
            // we CANNOT hanlde javascript, so just skip it
        	return;
        } catch (Exception e) {
            IaaUtil.log("  @@@@@@ Strange Exception: " + e.toString()
                        + " src = " + (String)asTagIMG.getAttribute(HTML.Attribute.SRC));
            return;
        }

        imgLink.setUserAgent(m_sUserAgent);

        String width = (String)asTagIMG.getAttribute(HTML.Attribute.WIDTH);
        if (width != null) {
            try {
                imgLink.setWidth(Integer.parseInt(width));
            } catch (NumberFormatException e) {
            // in case the page author use  width=100%
            // skip this error
            }
        }

        String height = (String)asTagIMG.getAttribute(HTML.Attribute.HEIGHT);
        if (height != null) {
            try {
                imgLink.setHeight(Integer.parseInt(height));
            } catch (NumberFormatException e) {
            // in case the page author use  height=100%
            // skip this error
            }
        }

        String title = (String)asTagIMG.getAttribute(HTML.Attribute.ALT);
        if (title != null) {
            imgLink.setAdTitle(title);
        }

        if(m_bIsPopupPage){
        	imgLink.setPopupParentURLStr(m_sParentPage);
        }
        
     	//add by ShengMengBai at 2005-04-15
        imgLink.setPublisherURL(m_publisherURL);
        
        m_objImpAnalyzer.addAdDataLink(imgLink);
        
        m_iImgLinkCount++;
        
        IaaDebug.end(1, "find ImgLink");
    }

    private void findFlashLink(boolean bObjectTag) {
        SimpleAttributeSet as = null;
        String sMovieURL = null;
        String sWidth = null;
        String sHeight = null;
        String sCodeBase = null;
        
        //find the flash through object tag
        try{
	        if(bObjectTag){
	        	as = (SimpleAttributeSet)m_objAttributeSet.getAttribute(HTML.Tag.OBJECT);
	        	if(as == null){
	        		return;
	        	}
		        String sID = (String)as.getAttribute(HTML.Attribute.CLASSID);
		        // whether is this object flash?  
		        /*if (sID != null && sID.toLowerCase().equals(FLASH_CLASSID)) {*/
		            
		            // Find the flash URL through movie PARAM
		        	SimpleAttributeSet sas = (SimpleAttributeSet)m_objAttributeSet.getAttribute(HTML.Tag.PARAM);
		        	if(sas != null){
			        	// get the flash URL.
			            sMovieURL = (String)sas.getAttribute(FLASH_MOVIE);
			            if (sMovieURL == null) {
			            	return;
			            }
				        sCodeBase = (String)as.getAttribute(HTML.Attribute.CODEBASE);
		        	}
		       /* }else{
		        	return;
		        }*/
	        }else{//find the flash through embed tag.
	        	as = (SimpleAttributeSet)m_objAttributeSet.getAttribute(EMBED);
	        	if(as == null){
	        		return;
	        	}
	        	String sType = (String)as.getAttribute(HTML.Attribute.TYPE);
	        	boolean bIsFlash = false;
	        	//is flash?
	        	if(sType != null && !sType.equals("application/x-shockwave-flash")){
	        		bIsFlash = true;;
	        	}
	        	// get the flash URL.
	            sMovieURL = (String)as.getAttribute(HTML.Attribute.SRC);
	            if (sMovieURL == null) {
	            	return;
	            }
	            //if there flash indicator and the scr don't contain ".swf", don't deal with it
	            if(!bIsFlash){
	            	if (sMovieURL.toLowerCase().indexOf(".swf") < 0){
	            		return;
	            	}
	            }
		        sCodeBase = "";
	        }
	    } catch (Exception e) {
	    	IaaUtil.printStackTrace(e);
	    	return;
	    }
        
	    //has connect to this URL.
	    if(m_urlList.contains(sMovieURL)){
	    	return;
	    }else{
	    	m_urlList.add(sMovieURL);
	    }
	    
	    IaaDebug.begin(1, "find FlashLink");
	    try {
	       	// Get the Width, Height and CodeBase
	        sWidth = (String)as.getAttribute(HTML.Attribute.WIDTH);
	        sHeight = (String)as.getAttribute(HTML.Attribute.HEIGHT);
	
	        FlashLink flashLink = null;
	        try {
	            URL url = URLUtil.getURL(m_pageURL, sMovieURL);
	            if(m_objImpAnalyzer.isDisallow(url)){
	                IaaUtil.log("Obey robots.txt: " + url);
	                return;
	            }
	        	flashLink = new FlashLink(url, m_pageURL, sCodeBase);
	        } catch (MalformedURLException e) {
	            return;
	        }
	        
	        //set width and height
	        if ( sWidth != null ){
	        	try {
	        		flashLink.setWidth( Integer.parseInt( sWidth ));
	        	} catch ( NumberFormatException e ){
	        		//sometime the height is param, in this conddition,
	        		//keep the default width=200
	        	}
	        }
	        if ( sHeight != null ){
	        	try {
	        		flashLink.setHeight( Integer.parseInt( sHeight ));
	        	} catch ( NumberFormatException e ){
	        		//sometime the width is param, in this conddition,
	        		//keep the default width=100
	        	}
	        }
	
	        if(m_bIsPopupPage){
	        	flashLink.setPopupParentURLStr(m_sParentPage);
	        }
	        
	        flashLink.setUserAgent(m_sUserAgent);
	     	//add by ShengMengBai at 2005-04-15
	        flashLink.setPublisherURL(m_publisherURL);
	
	        m_objImpAnalyzer.addAdDataLink(flashLink);
	        
	    } catch (Exception e) {
	    	IaaUtil.printStackTrace(e);
	    	return;
	    }
	    m_iFlashLinkCount++;
	    IaaDebug.end(1, "find FlashLink");
   }
    private void findAppletLink(){
        SimpleAttributeSet as = (SimpleAttributeSet)m_objAttributeSet.getAttribute(HTML.Tag.APPLET);
        if(as != null){
            IaaDebug.begin(1, "find AppletLink");
        	String sCode = (String)as.getAttribute(HTML.Attribute.CODE);
        	String sCodeBase = (String)as.getAttribute(HTML.Attribute.CODEBASE);
        	if(sCodeBase == null){
        		sCodeBase = m_pageURL.toExternalForm();
        		int iPos = sCodeBase.lastIndexOf("/");
        		sCodeBase = sCodeBase.substring(0,iPos + 1);
        	} else {
        		try{
        			URL codeBase = URLUtil.getURL(m_pageURL, sCodeBase);
        			sCodeBase = codeBase.toExternalForm();
        		}catch(MalformedURLException e){
        			//do nothing
        		}
        	}
        	String sArchive = (String)as.getAttribute(HTML.Attribute.ARCHIVE);
        	String sWidth =  (String)as.getAttribute(HTML.Attribute.WIDTH);
        	String sHeight =  (String)as.getAttribute(HTML.Attribute.HEIGHT);
        	
            AppletLink appletLink = null;
            try {
                if(sArchive == null){
                    return;
                }
                URL url = URLUtil.getURL(m_pageURL, sArchive);
                if(m_objImpAnalyzer.isDisallow(url)){
                    IaaUtil.log("Obey robots.txt: " + url);
                    return;
                }
            	appletLink = new AppletLink(url, m_pageURL);
            } catch (MalformedURLException e) {
                return;
            }
            appletLink.setUserAgent(m_sUserAgent);
            //set width and height
            if ( sWidth != null ){
            	try {
            		appletLink.setWidth( Integer.parseInt( sWidth ));
            	} catch ( NumberFormatException e ){
            		//
            	}
            }
            if ( sHeight != null ){
            	try {
            		appletLink.setHeight( Integer.parseInt( sHeight ));
            	} catch ( NumberFormatException e ){
            		//
            	}
            }
        	
        	SimpleAttributeSet sas = (SimpleAttributeSet)m_objAttributeSet.getAttribute(HTML.Tag.PARAM);
        	String sParam = "<applet code=\"" + sCode + 
					"\" codebase=\"" + sCodeBase;
        	if(sArchive != null){
        		sParam += "\" archive=\"" + sArchive;
        	}
        	sParam += "\" width=" + sWidth +
					" height=" + sHeight + ">\n";
        	if(sas != null){
        		Enumeration enumer = sas.getAttributeNames();
        		while(enumer.hasMoreElements()){
        			String sTemp = (String)enumer.nextElement();
        			sParam += "<param name=\"" + sTemp + "\" value=\"" + (String)sas.getAttribute(sTemp) + "\">\n";
        		}
        	}
        	sParam += "</applet>";
        	if(sParam.length() > 512){
        		IaaUtil.log("Can't save Applet param, it is too long:\n" + sParam);
        		return;
        	}
        	
        	appletLink.setAppletParamStr(sParam);
        	//add by ShengMengBai at 2005-04-15
        	appletLink.setPublisherURL(m_publisherURL);
        	
        	m_objImpAnalyzer.addAdDataLink(appletLink);
        	
            m_iAppletLinkCount++;
            IaaDebug.end(1, "find AppletLink");
        }
    }
    //++changsheng gong 2005-3-22
    private void findHrefLink(){
        SimpleAttributeSet asTagA = (SimpleAttributeSet)m_objAttributeSet.getAttribute(HTML.Tag.A);	
		if(asTagA == null){
		    return ;
		}
        
        SimpleAttributeSet asTagIMG = (SimpleAttributeSet)m_objAttributeSet.getAttribute(HTML.Tag.IMG);
        if(asTagIMG != null){
        	return;
        }
        
	    Object objHREF = asTagA.getAttribute(HTML.Attribute.HREF);
	    if(objHREF == null){
	        return;	
	    }
	    
	    IaaDebug.begin(1, "find hrefLink");
	    IaaUtil.log("hrefLink in parsercallback"+objHREF.toString());
        IaaUtil.log("hrefLink pageurl in parsercallback"+m_pageURL);
	    HrefLink hrefLink = null;
	    try {
	        
	        //++add by ShengMeng Bai at 2005-04-15
	        //if it is not adagency, don't parse it.
	        URL url = URLUtil.getURL(m_pageURL, objHREF.toString());
	        HrefParser hrefParser = new HrefParser(url , m_sUserAgent);
	        if(!hrefParser.isAdAgency(m_lstAgency)){
	            return;
	        }
            if(m_objImpAnalyzer.isDisallow(url)){
                IaaUtil.log("Obey robots.txt: " + url);
                return;
            }
	        //++add by ShengMeng Bai at 2005-04-15
            hrefLink = new HrefLink(hrefParser, m_pageURL);
        } catch (MalformedURLException e) {
            return;
        } catch (Exception e) {
            IaaUtil.log("  @@@@@@ Strange Exception: " + e.toString()
                        + " src = " + objHREF.toString());
            return;
        }

        hrefLink.setUserAgent(m_sUserAgent);
     	//add by ShengMengBai at 2005-04-15
        hrefLink.setPublisherURL(m_publisherURL);
    	
        m_objImpAnalyzer.addAdDataLink(hrefLink);
        m_iHrefLinkCount++;
        
        IaaDebug.end(1, "find hrefLink");
    }
    //--changsheng gong 2005-3-22
}
